package com.gee.spring.cloud.feign.flow.handler;

import com.gee.spring.cloud.feign.flow.binding.annotation.FlowArg;
import com.gee.spring.cloud.feign.flow.binding.annotation.TempVar;
import com.gee.spring.cloud.feign.flow.exception.ArgNotFoundException;
import com.gee.spring.cloud.feign.flow.exception.SkippedException;
import com.gee.spring.cloud.feign.flow.execute.ExecuteContext;
import com.gee.spring.cloud.feign.flow.result.CheckResult;
import com.gee.spring.cloud.feign.flow.result.ExecuteState;
import com.gee.spring.cloud.feign.flow.util.FlowUtil;
import com.gee.spring.cloud.feign.flow.work.AbstractWork;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * desc:
 *
 * @author gee wrote on  2021-01-14 19:52:51
 */
public class DefaultWorkHandler extends AbstractWorkHandler {

    /**
     * check the execute state of the previous to decide to execute or direct to be exceptional
     * @param currentWorkId the current work's id
     * @param currentExecuteState the current work's execute state
     * @param preWorks  the current work's previous works
     * @param executeContext the execute context
     * @return CheckResult
     */
    @Override
    public CheckResult checkPreWorks(String currentWorkId, AtomicInteger currentExecuteState,
                                     Map<String, AbstractWork<?>> preWorks,
                                     ExecuteContext executeContext) {
        if (preWorks.size() > 0) {

            AbstractWork<?> work = oneOfNecessaryException(currentWorkId, preWorks, executeContext);
            if (work != null && updExecuteStateInit2Working(currentExecuteState)){
                SkippedException skippedException = new SkippedException(PREVIOUS_NECESSARY_EXCEPTION + work.getId());
                return CheckResult.build(ExecuteState.EXCEPTIONAL, skippedException);
            }

            boolean allUnnecessaryException = allUnnecessaryException(currentWorkId, preWorks, executeContext);
            if (allUnnecessaryException
                    && updExecuteStateInit2Working(currentExecuteState)){
                String message = ALL_PREVIOUS_UNNECESSARY_EXCEPTION + getAllPreUnnecessaryIds(currentWorkId, preWorks);
                SkippedException skippedException = new SkippedException(message);
                return CheckResult.build(ExecuteState.EXCEPTIONAL, skippedException);
            }

            boolean anyNecessaryUnfinished = anyNecessaryUnfinished(currentWorkId, preWorks, executeContext);
            /*
                the current work decides not to execute or be exceptional's situations:
                1. the previous works have unfinished work which is necessary and if he current work has previous
                   unnecessary works
                2. the previous works have previous unnecessary works, and none of them executes successfully,
                    and exist at least one which is unfinished of them;
             */
            if (anyNecessaryUnfinished
                    || (anyUnnecessaryUnfinished(currentWorkId, preWorks,executeContext)
                    && noneUnnecessarySuccess(currentWorkId, preWorks, executeContext))){
                return CheckResult.build(ExecuteState.INIT);
            }
        }
        return updExecuteStateInit2Working(currentExecuteState)
                ? CheckResult.build(ExecuteState.WORKING) : CheckResult.build(ExecuteState.INIT);
    }

    @Override
    public void postBeforeBegin(String currentWorkId, Object param){
    }

    @Override
    public void postBeforeNextBegin(AbstractWork<?> currentWork, ExecuteState currentWorkExecuteState,
                                                  ExecuteContext executeContext){
        TempVar[] tempVars = currentWork.getTempVars();
        if (tempVars != null && tempVars.length > 0) {
            for (TempVar tempVar : tempVars) {
                Object result = calculateTempVar(tempVar, executeContext);
                executeContext.getTempVarMap().put(tempVar.varName(), result);
            }
        }
    }

    private Object calculateTempVar(TempVar tempVar, ExecuteContext executeContext) {
        FlowArg[] argAnnotations = tempVar.args();
        Method method = FlowUtil.getCalculatorMethod(tempVar);
        try {
            Object[] args = executeContext.getArgs(argAnnotations);
            return method.invoke(null, args);
        } catch (ArgNotFoundException | IllegalAccessException | InvocationTargetException ignored) {
            LOGGER.error("calculate tempVar [{}] exceptionally ", tempVar.varName());
        }
        return null;
    }

    @Override
    public void postAfterNextBegin(AbstractWork<?> currentWork, ExecuteContext executeContext) {
    }

}
